package com.lightboat.framework.config.wx;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.lightboat.common.config.properties.WxMpProperties;
import com.lightboat.system.handler.AbstractWxMpHandler;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.config.WxMpHostConfig;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * wechat mp configuration
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@AllArgsConstructor
@Configuration
@ConditionalOnBean(WxMpProperties.class)
@EnableConfigurationProperties(WxMpProperties.class)
public class WxMpConfiguration {
    private final WxMpProperties properties;
    private final StringRedisTemplate redisTemplate;

    @Bean
    public WxMpService wxMpService() {
        // 代码里 getConfigs()处报错的同学，请注意仔细阅读项目说明，你的IDE需要引入lombok插件！！！！
        final List<WxMpProperties.MpConfig> configs = this.properties.getConfigs();
        if (configs == null) {
            throw new RuntimeException("大哥，拜托先看下项目首页的说明（readme文件），添加下相关配置，注意别配错了！");
        }

        WxMpHostConfig hostConfig = BeanUtil.copyProperties(properties.getHostConfig(), WxMpHostConfig.class);

        WxMpService service = new WxMpServiceImpl();
        service.setMultiConfigStorages(configs
            .stream().map(a -> {
                WxMpDefaultConfigImpl configStorage;
                if (this.properties.isUseRedis()) {
                    configStorage = new WxMpRedisConfigImpl(new RedisTemplateWxRedisOps(redisTemplate), a.getAppId());
                } else {
                    configStorage = new WxMpDefaultConfigImpl();
                }
                configStorage.setHostConfig(hostConfig);
                configStorage.setAppId(a.getAppId());
                configStorage.setSecret(a.getSecret());
                configStorage.setToken(a.getToken());
                configStorage.setAesKey(a.getAesKey());
                return configStorage;
            }).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o)));
        return service;
    }

    /**
     * 微信消息路由器，通过代码化的配置，把来自微信的消息交给handler处理
     * @param wxMpService
     * @return
     */
    @Bean
    public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
        WxMpMessageRouter router = new WxMpMessageRouter(wxMpService);
        // 记录所有事件的日志 （异步执行）
        router.rule().handler(new AbstractWxMpHandler() {
            /**
             * 处理微信推送消息.
             *
             * @param wxMessage      微信推送消息
             * @param context        上下文，如果handler或interceptor之间有信息要传递，可以用这个
             * @param wxMpService    服务类
             * @param sessionManager session管理器
             * @return xml格式的消息 ，如果在异步规则里处理的话，可以返回null
             * @throws WxErrorException 异常
             */
            @Override
            public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
                logger.info("\n接收到请求消息，内容：{},{}", JSON.toJSONString(wxMessage),JSON.toJSONString(context));
                return null;
            }
        }).next();

        return router;
    }

}